Dealing with data
The tidyverse
is an eco-system of packages that provides a consistent, intuitive
system for data manipulation and visualisation in R.

A spreadsheet that we would normally analyse in Excel can can be
imported into R by using a function from the readr package.
The readxl package can also import files in Excel’s own format
We will use the file tcga_clinical_CLEANED.tsv (11MB) as
an example. This file contains information about biological samples that
were included as part of the TCGA (The Cancer Genome Atlas) project -
all tumour types.
If you do not have this file, it can be downloaded with the following
code. N.B. the code will first check if the file is present or not.
if(!file.exists("tcga_clinical_CLEANED.tsv")) download.file(url = "https://sbc.shef.ac.uk/r4biol/tcga_clinical_CLEANED.tsv",
destfile = "tcga_clinical_CLEANED.tsv")
As the file name ends in .tsv the function we want is
probably read_tsv
library(readr)
clinical <- read_tsv("tcga_clinical_CLEANED.tsv")
If you got an error could not find function "read_tsv"
at the previous step, it will because you don’t have the
readr package installed. In which you will need to run the
command:-
install.packages("readr")
library(readr)
This will install the readr package. You will then need
to run the read_tsv line of code from above.
The Environment panel of RStudio (top-right) should show that an
object called clinical has been created. This means that we
can start doing some analysis on this dataset. The choice to call the
object clinical was ours. We could have used any name
instead of clinical but we chose something vaguely
informative and memorable.
The dimensions of the object should should
7706 obs. of 420 variables. This means the object we have
created contains 7706 rows and 420 columns. Each row is a different
observation; in this case a different biological sample. Each column
records the value of a different variable. In R’s terminology, we have
just created a tibble which is a special case of something
called a data.frame. As we will see the the object can
contain either numbers or text in each column.
RStudio has a browsing functionality available that we can activate
using the View function
View(clinical)
Observations that are missing are indicated by an NA. It
may look at first glance that the table mostly contains missing data.
This is because some columns are only recorded for particular tumour
types and not all tumour types recorded the same information.
Data Exploration
We will now start to explore the data with a couple of different
questions in mind:-
- What are the differences in male/female split between
different tumour types?
- Is there a difference in diagnosis age in different tumour
types?
The tasks required to do this will be greatly helped by the
dplyr package.
library(dplyr)
As with the readr package above, you will need to have
installed the dplyr package beforehand.
install.packages("dplyr")
Choosing what columns to analyse
The select function allows us to narrow down the number
of variables we are interested in from 420. The first argument is always
the name of the data frame. There are numerous different ways of
specifying which column(s) you want, including typing the names of the
columns of interest in exactly the same way that they appear in the
data. Let’s assume we already know the names of columns containing
tumour type and gender.
##Note that the spelling of "tumor" has to exactly match that found in the data
select(clinical,
tumor_tissue_site,
gender
)
We can reasonably guess that the Age information would be contained
in a column that has “age” somewhere in the name. However, the column is
not just called “age” or “Age”
select(clinical, age)
select(clinical, Age)
Without manually going through the columns, there are a few “helper”
functions that we can employ
select(clinical, contains("age"))
select(clinical, contains("age_"))
select(clinical, starts_with("age"))
Up to now we have not changed the underlying dataset.
select is showing what the dataset looks like after
applying the specified subset. If we want to make permanent changes we
can create a variable
analysis_data <- select(clinical,
bcr_patient_barcode,
tumor_tissue_site,
gender,
age_at_diagnosis)
Restricting rows
The select function only performs the very specific task
of letting you choose what columns you want to analyse. After using
select, our dataset analysis_data still has
all 2496 rows.
The function to choose or restrict to the rows we might be interested
in is called filter. We have to write a short R command to
choose the rows.
e.g. if we want only the male samples we use the following code.
Notice that two “=” signs are required. If you try and use the function
with a single “=” R will print a helpful hint.
filter(analysis_data, gender == "MALE")
and the females
filter(analysis_data, gender == "FEMALE")
An equivalent, but somewhat unnecessary for this example, statement
would be to ask for rows where the gender is not equal
to MALE
filter(analysis_data, gender != "MALE")
We can also restrict the data based on numeric columns by using
>, < etc.
filter(analysis_data, age_at_diagnosis > 80)
But what if we want males with Brain tumours? dplyr
allows us to combine more than one condition if we separate them with a
,. In computing this is known as an “and” statement and
only rows where both statements are two will be shown.
The line could be extended to include more than two tests if we wanted
to.
filter(analysis_data, gender == "MALE",tumor_tissue_site == "Brain")
How about brain or lung tumours? Using a | symbol
instead of a , allows for either of two (or more)
conditions to be TRUE.
filter(analysis_data, tumor_tissue_site == "Brain" | tumor_tissue_site == "Lung")
To answer the question of how many males / females have a certain
tumour type we could now use R statements such as:-
filter(analysis_data, gender == "MALE",tumor_tissue_site == "Brain")
filter(analysis_data, gender == "FEMALE",tumor_tissue_site == "Brain")
and make a note of the number of observations included in the
resulting data frame. However, there is much more flexible way of
summarising data in this manner.
Summarising
Although useful for data exploration, it would clearly be inefficient
to get gender/tumour type counts in this way as we would have to repeat
for all combinations of tumour type and gender. The function
count can now give us exactly what we want. The output is
given as a tibble, so we could use some of the functions
that we have learnt about so far (select,
filter…) to further manipulate. e.g. obtain the counts for
just Brain/
count(analysis_data,
tumor_tissue_site,
gender)
The count function is useful for tabulating the number
of observations, but for other summary statistics a more general
sumamrise function can be used. This can be used in
conjunction with basic summary functions supported by base R. A summary
statistic being something that can be applied to a series of numbers and
produce a single number as a result. e.g. the average, minimum, maximum
etc.
summarise(analysis_data,
Average = mean(age_at_diagnosis),
min = min(age_at_diagnosis),
max = max(age_at_diagnosis))
However, we have a problem due to missing values. If R sees and
missing values in a column it will report the mean, minimum or maximum
of that column as a missing value. Although this default behaviour can
be changed, before proceeding we could also choose to remove any missing
observations from the data. These are represented by a NA
value, which is a special value and not a character label.
filter(analysis_data, is.na(age_at_diagnosis) | is.na(tumor_tissue_site))
We want the opposite of the above; where the age of diagnosis and
tumour site is not missing.
analysis_data <- filter(analysis_data, !is.na(age_at_diagnosis), !is.na(tumor_tissue_site))
The summary will now work as expected.
summarise(analysis_data,
Average = mean(age_at_diagnosis),
min = min(age_at_diagnosis),
max = max(age_at_diagnosis))
This might not be what we want in all circumstances, as the
statistics can also be calculated on a per-tumour site basis using
dplyrs group_by function.
data_grouped <- group_by(analysis_data, tumor_tissue_site)
summarise(data_grouped,Average = mean(age_at_diagnosis),
min = min(age_at_diagnosis),
max = max(age_at_diagnosis) )
Sorting (arranging)
We have previously used filter to restrict the rows that
we are interested in. Rather than just analysing the male or female
patients (for example), we might also want the rows in our table to be
ordered according to the gender column.
arrange(analysis_data, gender)
We can also arrange by columns containing numeric values in either
ascending (the default) or descending order.
arrange(analysis_data, age_at_diagnosis)
Like how sorting works in Excel, we can also use mutliple columns for
sorting. e.g. if we want ordering by diagnosis age for each tumour type
separately.
Workflows and “piping”
So far we have used several operations in isolation. However, the
real joy (?) of dplyr is how different operations can be
chained together. Lets say we just wanted female tumours.
filter(analysis_data, gender == "FEMALE")
Our next step could be to remove the gender column since
it is somewhat redundant.
analysis_data2 <- filter(analysis_data, gender == "FEMALE")
select(analysis_data2, tumor_tissue_site, age_at_diagnosis)
## or
## select(analysis_data2, -gender)
The code would quickly get cumbersome if we wanted to include
additional steps such as removing NA values. An alternative
approach called “piping” is recommended and activated by adding
%>% at the end of a line. This tells R to use the output
of the current line as the first argument on the next line. In this
current example it means we don’t need to specify which data frame that
select uses as input - it will use the data frame created
by the filter in the previous line. The code written using
%>% is more concise.
filter(analysis_data, gender == "FEMALE") %>% ## and then...
select(tumor_tissue_site, age_at_diagnosis) ## %>% and then...
We recently created a summary table for each tumour type giving the
average, minimum and maximum of diagnosis age. This can be replicated
using %>% and an extra sorting step added to the
end.
group_by(analysis_data, tumor_tissue_site) %>%
summarise(Average = mean(age_at_diagnosis),
min = min(age_at_diagnosis),
max = max(age_at_diagnosis)) %>%
arrange(Average)
Overview of plotting
Our recommending way of creating plots in RStudio is to use the
ggplot2 package - especially as it interacts well with
dplyr and other tidyverse packages.
library(ggplot2)
A couple of useful references are given here-
The general principle of creating a plot is the same regardless of
what kind of plot we want to make
- specify the
data frame containing the data we want to
plot
- specify which columns in that data frame we want to use for various
aesthetic aspects of the plot
- define the type of plot we want
- apply any additional format changes
A bar plot would be a natural choice for showing the counts of male /
female samples. The geom_bar plot will automatically count
how many occurrences there are for each value.
ggplot(analysis_data, aes(x = gender)) + geom_bar()
Numerical data can be visualised using a density plot or histogram.
The density is automatically calculated and displayed on the y-axis.

In order to compare the age distributions of different tumour types
we can also imagine this being displayed as a series of boxplots
with
- the age variable on the y-axis
- the type of tumour on the x-axis
this can be translated into ggplot2 language as follows
-
ggplot(analysis_data, aes(x = tumor_tissue_site, y = age_at_diagnosis)) + geom_boxplot()

A disadvantage of the boxplot is that it only gives a very crude
summary of the data. It can be misleading when applied to data with few
observations and is often preferable to add individual data points
ggplot(analysis_data, aes(x = tumor_tissue_site, y = age_at_diagnosis)) + geom_boxplot() + geom_jitter(width=0.1)

Adding some colour to the plot can be achieved by adding a
fill aesthetic and specifying what column to map the
colours too. A colour palette is automatically chosen, but can be
changed afterwards if we wish.
ggplot(analysis_data, aes(x = tumor_tissue_site, y = age_at_diagnosis, fill = tumor_tissue_site)) + geom_boxplot() + geom_jitter(width=0.1)

Adding the fill aesthetic for the density plot can be
used to show a separate curve for each tumour type.

Another useful technique for splitting the plots based on a variable
is to use the facet_wrap function that will give a grid of
plots. For instance we can show male/female counts for each tumour type
separately.
ggplot(analysis_data, aes(x = gender,fill=gender)) + geom_bar() + facet_wrap(~tumor_tissue_site)

By combining all the techniques we have seen we can compare the
diagnosis age between males and females; separately for each tumour
type.
ggplot(analysis_data, aes(x =gender, y = age_at_diagnosis, fill = gender)) + geom_boxplot() + geom_jitter(width=0.1) + facet_wrap(~tumor_tissue_site)

Challenges of “messy” data
Real-life data are often less straightforward to deal with than the
“cleaned” dataset presented here. Despite the many high-throughput
technologies that are used for scientific investigation, there is
inevitably a spreadsheet(s) needed to describe the experimental setup
and this is typically entered manually.
So-called “Data Wrangling” is a crucial and time-consuming part of
the analysis process taking 80% of analysis time by some estimates.
Hadley Wickham, Chief Scientist at Posit and lead author of
ggplot2 likens tidy and messy data to Leo Tolstoy’s quote
about families:-
Happy families are all alike; every unhappy family is unhappy in its
own way
Like families, tidy datasets are all alike but every messy dataset is
messy in its own way.
A comprehensive guide to the issues surrounding data entry via
spreadsheets, and how to avoid them, is given by Data Carpentry.
However, for public data that we have no control over we often have
no choice but to clean the data ourselves. We have intentionally created
an alternative dataset with a few intentional issues to illustrate the
cleaning process.
messy <- read_tsv("tcga_clinical_MESSY.tsv")
messy
NA
Whitespace
“whitespace” is the addition of a blank character or space to the
beginning or end of text. Traditionally it is a problem because it will
create extra categories in your data. e.g. MALE and
MALE. The messy dataset that you have just imported
includes some whitespace in the tumor_tissue_site column.
However, the read_tsv function automatically ignores
whitespace values as the trim_ws argument of
read_tsv is set to TRUE (see the help page
?read_tsv).
messy_ws <- read_tsv("tcga_clinical_MESSY.tsv",
trim_ws = FALSE)
messy_ws
count(messy_ws,tumor_tissue_site)
The resulting data frame now contains two apparently identical
categories for Bladder. However, with the use of the
nchar function, which counts the number of characters, we
can see that extra spaces must be included.
count(messy_ws,tumor_tissue_site) %>%
mutate("Length_of_Label" = nchar(tumor_tissue_site))
Clearly we could have used the default settings for
read_tsv and the problem would not have occurred.
Otherwise, it is useful to know about the stringr package
that contains many useful functions for cleaning character data.
For the example of removing whitespace we can use the
str_trim function combined with a mutate. This
will replace all the whitespace in the tumor_tissue_site
column and overwrite the column. If we repeat a count afterwards we see
only the unique entries that we expect.
library(stringr)
mutate(messy_ws, tumor_tissue_site = str_trim(tumor_tissue_site)) %>%
count(tumor_tissue_site)
Inconsistent coding of variables
Unfortunately the tumor_tissue_site column is not the
only one with issue that need fixing with these data. If, as before, we
try and plot the number of males/females in the dataset we get a
surprise.
ggplot(messy, aes(x = gender)) + geom_bar()

There is no differentiation between female and
FEMALE or male and MALE. Whilst
we can intuitively decide that these represent the same value, they do
not get automatically combined in R. The consequence being that attempts
to identify all the male patients will require some careful coding. The
example used previously will now no longer identify all the correct
patients.
filter(messy, gender == "MALE")
One solution when filtering would be to add different criteria to
account for the different capitalisation
filter(messy, gender == "MALE" | gender == "male")
However, since we know that the error is due to inconsistent use of
upper/lowercase we can use the str_to_upper function in
stringr to convert all values to uppercase. Or indeed we
could convert all to lowercase using str_to_lower if we
prefered.
messy %>%
mutate(gender = str_to_upper(gender)) %>%
ggplot(aes(x = gender)) + geom_bar()

We would probably want to also make the change permanent by creating
a new variable
cleaned <- read_tsv("tcga_clinical_MESSY.tsv") %>%
mutate(gender = str_to_upper(gender))
A more generic approach would be to use the forcats
package to replace all occurrences of male with
MALE and the same for females.
The package allows us to “recode” entries in a column that contains a
factor. i.e. categorical.
library(forcats)
mutate(messy, gender = forcats::fct_recode(gender,"MALE"="male"),
gender = forcats::fct_recode(gender,"FEMALE"="female")) %>%
ggplot(aes(x = gender)) + geom_bar()

The approach is more flexible than merely changing the case, as it
could also replace other values such as “m” or “f” if they existed.
## Just example code if we wanted to replace "m" with "MALE"
messy <- messy %>%
mutate(gender = fct_recode(gender,
"MALE" = "male",
"MALE" = "m"
))
Different means of representing missing values
There are many different strategies for representing missing data.
The read_tsv function should automatically detect any
NA values in the source dataset and treat them
appropriately. However, in our messy dataset we also have
NULL values (as seen by making a count of the values in
age_at_diagnosis).
count(messy, age_at_diagnosis) %>% arrange(desc(n))
Because the NULL value is present in the
age_at_diagnosis column, R will treat. the entire column as
containing characters. Therefore we cannot use the kind of plots we
would expect with numeric data
ggplot(messy, aes(x = age_at_diagnosis)) + geom_histogram()
Likewise we can’t calculate numeric summaries; although R will
attempt to and create a data frame of NA values rather than
giving an error.
group_by(messy, tumor_tissue_site) %>%
summarise(Mean_Diagnosis_Age = mean(age_at_diagnosis,na.rm=TRUE))
Warning: There were 19 warnings in `summarise()`.
The first warning was:
ℹ In argument: `Mean_Diagnosis_Age = mean(age_at_diagnosis, na.rm =
TRUE)`.
ℹ In group 1: `tumor_tissue_site = "Bladder"`.
Caused by warning in `mean.default()`:
! argument is not numeric or logical: returning NA
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 18 remaining warnings.
The read_tsv function has the ability to replace NA
values when the data are imported. Specifically, the na
argument can be used to define what values are being used to represent
missing.
read_tsv("tcga_clinical_MESSY.tsv", na = c("NULL","NA")) %>%
ggplot(aes(x = age_at_diagnosis)) + geom_histogram()

read_tsv("tcga_clinical_MESSY.tsv", na = c("NULL","NA")) %>%
group_by(tumor_tissue_site) %>%
summarise(Mean_Diagnosis_Age = mean(age_at_diagnosis,na.rm=TRUE))
Including units in the column
The final column in this example contains height information (where
available) for our patients. Clearly it is important to know what units
this is recorded in, but placing the units inside the entries creates
issues as we can’t treat the data as numbers.
arrange(messy,height_at_diagnosis)
The stringr package can be used again, and this time a
function called str_remove_all which removes all
occurrences of a particular string. In particular we want to remove
cm. We will need an additional step to convert the column
into numeric values
messy %>%
mutate(height_at_diagnosis=str_remove_all(height_at_diagnosis, "cm")) %>%
mutate(height_at_diagnosis = as.numeric(height_at_diagnosis)) %>%
arrange(height_at_diagnosis)
There is usually more than one way of completing a task in R. In this
instance, we could also use the str_sub function in
stringr to extract a “substring” from each entry in the
column. The argument end=-3 specifies that the extraction
should end three characters before the end of each string.
messy %>%
mutate(height_at_diagnosis=str_sub(height_at_diagnosis, end=-3)) %>%
mutate(height_at_diagnosis = as.numeric(height_at_diagnosis)) %>%
arrange(height_at_diagnosis)
NA
NA
Final code to clean the data
For reference, here is the final code chunk that can be used to clean
the data.
cleaned <- read_tsv("tcga_clinical_MESSY.tsv", na = c("NULL","NA")) %>%
mutate(messy, gender = forcats::fct_recode(gender,"MALE"="male"),
gender = forcats::fct_recode(gender,"FEMALE"="female")) %>%
mutate(height_at_diagnosis=str_sub(height_at_diagnosis, end=-3)) %>%
mutate(height_at_diagnosis = as.numeric(height_at_diagnosis))
LS0tCnRpdGxlOiAiUiBJbnRyb2R1Y3Rpb24gZm9yIEJpb2xvZ3kiCmF1dGhvcjogIk1hcmsgRHVubmluZyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLG1lc3NhZ2U9RkFMU0UpCmBgYAoKCiMgRGVhbGluZyB3aXRoIGRhdGEKClRoZSBbKioqdGlkeXZlcnNlKioqXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnLykgaXMgYW4gZWNvLXN5c3RlbSBvZiBwYWNrYWdlcyB0aGF0IHByb3ZpZGVzIGEgY29uc2lzdGVudCwgaW50dWl0aXZlIHN5c3RlbSBmb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGlzYXRpb24gaW4gUi4KCgohW10oaHR0cHM6Ly9hYmVyZGVlbnN0dWR5Z3JvdXAuZ2l0aHViLmlvL3N0dWR5R3JvdXAvbGVzc29ucy9TRy1UMi1Kb2ludFdvcmtzaG9wL3RpZHl2ZXJzZS5wbmcpCgpBIHNwcmVhZHNoZWV0IHRoYXQgd2Ugd291bGQgbm9ybWFsbHkgYW5hbHlzZSBpbiBFeGNlbCBjYW4gY2FuIGJlIGltcG9ydGVkIGludG8gUiBieSB1c2luZyBhICpmdW5jdGlvbiogZnJvbSB0aGUgcmVhZHIgcGFja2FnZS4KCi0gYHJlYWRfY3N2YAotIGByZWFkX3RzdmAKClRoZSByZWFkeGwgcGFja2FnZSBjYW4gYWxzbyBpbXBvcnQgZmlsZXMgaW4gRXhjZWwncyBvd24gZm9ybWF0CgotIGByZWFkX3hsc2AKLSBgcmVhZF94bHN4YAoKV2Ugd2lsbCB1c2UgdGhlIGZpbGUgYHRjZ2FfY2xpbmljYWxfQ0xFQU5FRC50c3ZgICgxMU1CKSBhcyBhbiBleGFtcGxlLiBUaGlzIGZpbGUgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgYmlvbG9naWNhbCBzYW1wbGVzIHRoYXQgd2VyZSBpbmNsdWRlZCBhcyBwYXJ0IG9mIHRoZSBUQ0dBIChUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcykgcHJvamVjdCAtIGFsbCB0dW1vdXIgdHlwZXMuCgpJZiB5b3UgZG8gbm90IGhhdmUgdGhpcyBmaWxlLCBpdCBjYW4gYmUgZG93bmxvYWRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZS4gTi5CLiB0aGUgY29kZSB3aWxsIGZpcnN0IGNoZWNrIGlmIHRoZSBmaWxlIGlzIHByZXNlbnQgb3Igbm90LgoKYGBge3J9CmlmKCFmaWxlLmV4aXN0cygidGNnYV9jbGluaWNhbF9DTEVBTkVELnRzdiIpKSBkb3dubG9hZC5maWxlKHVybCA9ICJodHRwczovL3NiYy5zaGVmLmFjLnVrL3I0YmlvbC90Y2dhX2NsaW5pY2FsX0NMRUFORUQudHN2IiwgCiAgICAgICAgICAgICAgZGVzdGZpbGUgPSAidGNnYV9jbGluaWNhbF9DTEVBTkVELnRzdiIpCmBgYAoKCkFzIHRoZSBmaWxlIG5hbWUgZW5kcyBpbiBgLnRzdmAgdGhlIGZ1bmN0aW9uIHdlIHdhbnQgaXMgcHJvYmFibHkgYHJlYWRfdHN2YAoKYGBge3Igd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHJlYWRyKQpjbGluaWNhbCA8LSByZWFkX3RzdigidGNnYV9jbGluaWNhbF9DTEVBTkVELnRzdiIpCmBgYAoKSWYgeW91IGdvdCBhbiBlcnJvciBgY291bGQgbm90IGZpbmQgZnVuY3Rpb24gInJlYWRfdHN2ImAgYXQgdGhlIHByZXZpb3VzIHN0ZXAsIGl0IHdpbGwgYmVjYXVzZSB5b3UgZG9uJ3QgaGF2ZSB0aGUgYHJlYWRyYCBwYWNrYWdlIGluc3RhbGxlZC4gSW4gd2hpY2ggeW91IHdpbGwgbmVlZCB0byBydW4gdGhlIGNvbW1hbmQ6LQoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQpsaWJyYXJ5KHJlYWRyKQpgYGAKClRoaXMgd2lsbCBpbnN0YWxsIHRoZSBgcmVhZHJgIHBhY2thZ2UuIFlvdSB3aWxsIHRoZW4gbmVlZCB0byBydW4gdGhlIGByZWFkX3RzdmAgbGluZSBvZiBjb2RlIGZyb20gYWJvdmUuCgoKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KSWYgeW91IGdldCByZWFsbHkgc3R1Y2sgcmVhZGluZyBkYXRhIGludG8gUiwgeW91IGNhbiB1c2UgdGhlIEltcG9ydCBEYXRhc2V0IG9wdGlvbiBmcm9tIHRoZSBGaWxlIG1lbnUgd2hpY2ggd2lsbCBhbGxvdyB5b3UgdG8gY2hvb3NlIHRoZSBwYXJhbWV0ZXJzIHRvIHJlYWQgdGhlIGRhdGEgaW50ZXJhY3RpdmVseQo8L2Rpdj4KClRoZSBFbnZpcm9ubWVudCBwYW5lbCBvZiBSU3R1ZGlvICh0b3AtcmlnaHQpIHNob3VsZCBzaG93IHRoYXQgYW4gb2JqZWN0IGNhbGxlZCBgY2xpbmljYWxgIGhhcyBiZWVuIGNyZWF0ZWQuIFRoaXMgbWVhbnMgdGhhdCB3ZSBjYW4gc3RhcnQgZG9pbmcgc29tZSBhbmFseXNpcyBvbiB0aGlzIGRhdGFzZXQuIFRoZSBjaG9pY2UgdG8gY2FsbCB0aGUgb2JqZWN0IGBjbGluaWNhbGAgd2FzIG91cnMuIFdlIGNvdWxkIGhhdmUgdXNlZCBhbnkgbmFtZSBpbnN0ZWFkIG9mIGBjbGluaWNhbGAgYnV0IHdlIGNob3NlIHNvbWV0aGluZyB2YWd1ZWx5IGluZm9ybWF0aXZlIGFuZCBtZW1vcmFibGUuCgpUaGUgZGltZW5zaW9ucyBvZiB0aGUgb2JqZWN0IHNob3VsZCBzaG91bGQgYDc3MDYgb2JzLiBvZiA0MjAgdmFyaWFibGVzYC4gVGhpcyBtZWFucyB0aGUgb2JqZWN0IHdlIGhhdmUgY3JlYXRlZCBjb250YWlucyA3NzA2IHJvd3MgYW5kIDQyMCBjb2x1bW5zLiBFYWNoIHJvdyBpcyBhIGRpZmZlcmVudCBvYnNlcnZhdGlvbjsgaW4gdGhpcyBjYXNlIGEgZGlmZmVyZW50IGJpb2xvZ2ljYWwgc2FtcGxlLiBFYWNoIGNvbHVtbiByZWNvcmRzIHRoZSB2YWx1ZSBvZiBhIGRpZmZlcmVudCB2YXJpYWJsZS4gSW4gUidzIHRlcm1pbm9sb2d5LCB3ZSBoYXZlIGp1c3QgY3JlYXRlZCBhIGB0aWJibGVgIHdoaWNoIGlzIGEgc3BlY2lhbCBjYXNlIG9mIHNvbWV0aGluZyBjYWxsZWQgYSBgZGF0YS5mcmFtZWAuIEFzIHdlIHdpbGwgc2VlIHRoZSB0aGUgb2JqZWN0IGNhbiBjb250YWluIGVpdGhlciBudW1iZXJzIG9yIHRleHQgaW4gZWFjaCBjb2x1bW4uCgpSU3R1ZGlvIGhhcyBhIGJyb3dzaW5nIGZ1bmN0aW9uYWxpdHkgYXZhaWxhYmxlIHRoYXQgd2UgY2FuIGFjdGl2YXRlIHVzaW5nIHRoZSBgVmlld2AgZnVuY3Rpb24KCmBgYHtyfQpWaWV3KGNsaW5pY2FsKQpgYGAKCk9ic2VydmF0aW9ucyB0aGF0IGFyZSBtaXNzaW5nIGFyZSBpbmRpY2F0ZWQgYnkgYW4gYE5BYC4gSXQgbWF5IGxvb2sgYXQgZmlyc3QgZ2xhbmNlIHRoYXQgdGhlIHRhYmxlIG1vc3RseSBjb250YWlucyBtaXNzaW5nIGRhdGEuIFRoaXMgaXMgYmVjYXVzZSBzb21lIGNvbHVtbnMgYXJlIG9ubHkgcmVjb3JkZWQgZm9yIHBhcnRpY3VsYXIgdHVtb3VyIHR5cGVzIGFuZCBub3QgYWxsIHR1bW91ciB0eXBlcyByZWNvcmRlZCB0aGUgc2FtZSBpbmZvcm1hdGlvbi4KCiMgRGF0YSBFeHBsb3JhdGlvbgoKV2Ugd2lsbCBub3cgc3RhcnQgdG8gZXhwbG9yZSB0aGUgZGF0YSB3aXRoIGEgY291cGxlIG9mIGRpZmZlcmVudCBxdWVzdGlvbnMgaW4gbWluZDotCgotICoqV2hhdCBhcmUgdGhlIGRpZmZlcmVuY2VzIGluIG1hbGUvZmVtYWxlIHNwbGl0IGJldHdlZW4gZGlmZmVyZW50IHR1bW91ciB0eXBlcz8qKgotICoqSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGluIGRpYWdub3NpcyBhZ2UgaW4gZGlmZmVyZW50IHR1bW91ciB0eXBlcz8qKgoKVGhlIHRhc2tzIHJlcXVpcmVkIHRvIGRvIHRoaXMgd2lsbCBiZSBncmVhdGx5IGhlbHBlZCBieSB0aGUgYGRwbHlyYCBwYWNrYWdlLgoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmBgYAoKQXMgd2l0aCB0aGUgYHJlYWRyYCBwYWNrYWdlIGFib3ZlLCB5b3Ugd2lsbCBuZWVkIHRvIGhhdmUgaW5zdGFsbGVkIHRoZSBgZHBseXJgIHBhY2thZ2UgYmVmb3JlaGFuZC4gCgpgYGB7ciBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCgpgYGAKCgojIyBDaG9vc2luZyB3aGF0IGNvbHVtbnMgdG8gYW5hbHlzZQoKVGhlIGBzZWxlY3RgIGZ1bmN0aW9uIGFsbG93cyB1cyB0byBuYXJyb3cgZG93biB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcyB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBmcm9tIDQyMC4gVGhlIGZpcnN0IGFyZ3VtZW50IGlzIGFsd2F5cyB0aGUgbmFtZSBvZiB0aGUgZGF0YSBmcmFtZS4gVGhlcmUgYXJlIG51bWVyb3VzIGRpZmZlcmVudCB3YXlzIG9mIHNwZWNpZnlpbmcgd2hpY2ggY29sdW1uKHMpIHlvdSB3YW50LCBpbmNsdWRpbmcgdHlwaW5nIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyBvZiBpbnRlcmVzdCAqaW4gZXhhY3RseSB0aGUgc2FtZSB3YXkgdGhhdCB0aGV5IGFwcGVhciBpbiB0aGUgZGF0YSouIExldCdzIGFzc3VtZSB3ZSBhbHJlYWR5IGtub3cgdGhlIG5hbWVzIG9mIGNvbHVtbnMgY29udGFpbmluZyB0dW1vdXIgdHlwZSBhbmQgZ2VuZGVyLgoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpUaGUgZGF0YXNldCB1dGlsaXplcyBhIGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBvZiBnZW5kZXIgYXMgJ21hbGUnIG9yICdmZW1hbGUnLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoaXMgY2F0ZWdvcml6YXRpb24gbWF5IG5vdCBmdWxseSBlbmNvbXBhc3MgdGhlIGRpdmVyc2UgcmFuZ2Ugb2YgZ2VuZGVyIGlkZW50aXRpZXMgcmVjb2duaXplZCB0b2RheS4KPC9kaXY+CgpgYGB7cn0KIyNOb3RlIHRoYXQgdGhlIHNwZWxsaW5nIG9mICJ0dW1vciIgaGFzIHRvIGV4YWN0bHkgbWF0Y2ggdGhhdCBmb3VuZCBpbiB0aGUgZGF0YQpzZWxlY3QoY2xpbmljYWwsIAogICAgICAgdHVtb3JfdGlzc3VlX3NpdGUsCiAgICAgICBnZW5kZXIKICAgICAgICkKYGBgCldlIGNhbiByZWFzb25hYmx5IGd1ZXNzIHRoYXQgdGhlIEFnZSBpbmZvcm1hdGlvbiB3b3VsZCBiZSBjb250YWluZWQgaW4gYSBjb2x1bW4gdGhhdCBoYXMgImFnZSIgc29tZXdoZXJlIGluIHRoZSBuYW1lLiBIb3dldmVyLCB0aGUgY29sdW1uIGlzIG5vdCBqdXN0IGNhbGxlZCAiYWdlIiBvciAiQWdlIgoKYGBge3IgZXZhbD1GQUxTRX0Kc2VsZWN0KGNsaW5pY2FsLCBhZ2UpCnNlbGVjdChjbGluaWNhbCwgQWdlKQpgYGAKCldpdGhvdXQgbWFudWFsbHkgZ29pbmcgdGhyb3VnaCB0aGUgY29sdW1ucywgdGhlcmUgYXJlIGEgZmV3ICJoZWxwZXIiIGZ1bmN0aW9ucyB0aGF0IHdlIGNhbiBlbXBsb3kKCmBgYHtyfQpzZWxlY3QoY2xpbmljYWwsIGNvbnRhaW5zKCJhZ2UiKSkKc2VsZWN0KGNsaW5pY2FsLCBjb250YWlucygiYWdlXyIpKQpzZWxlY3QoY2xpbmljYWwsIHN0YXJ0c193aXRoKCJhZ2UiKSkKYGBgCgpVcCB0byBub3cgd2UgaGF2ZSBub3QgY2hhbmdlZCB0aGUgdW5kZXJseWluZyBkYXRhc2V0LiBgc2VsZWN0YCBpcyBzaG93aW5nIHdoYXQgdGhlIGRhdGFzZXQgbG9va3MgbGlrZSBhZnRlciBhcHBseWluZyB0aGUgc3BlY2lmaWVkIHN1YnNldC4gSWYgd2Ugd2FudCB0byBtYWtlIHBlcm1hbmVudCBjaGFuZ2VzIHdlIGNhbiBjcmVhdGUgYSB2YXJpYWJsZQoKYGBge3J9CmFuYWx5c2lzX2RhdGEgPC0gc2VsZWN0KGNsaW5pY2FsLAogICAgICAgICAgICAgICAgICAgICAgICBiY3JfcGF0aWVudF9iYXJjb2RlLAogICAgICAgICAgICAgICAgICAgICAgICB0dW1vcl90aXNzdWVfc2l0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZGVyLAogICAgICAgICAgICAgICAgICAgICAgICBhZ2VfYXRfZGlhZ25vc2lzKQpgYGAKCiMjIFJlc3RyaWN0aW5nIHJvd3MKClRoZSBgc2VsZWN0YCBmdW5jdGlvbiBvbmx5IHBlcmZvcm1zIHRoZSB2ZXJ5IHNwZWNpZmljIHRhc2sgb2YgbGV0dGluZyB5b3UgY2hvb3NlIHdoYXQgY29sdW1ucyB5b3Ugd2FudCB0byBhbmFseXNlLiBBZnRlciB1c2luZyBgc2VsZWN0YCwgb3VyIGRhdGFzZXQgYGFuYWx5c2lzX2RhdGFgIHN0aWxsIGhhcyBhbGwgYHIgbnJvdyhhbmFseXNpc19kYXRhKWAgcm93cy4gCgpUaGUgZnVuY3Rpb24gdG8gY2hvb3NlIG9yIHJlc3RyaWN0IHRvIHRoZSByb3dzIHdlIG1pZ2h0IGJlIGludGVyZXN0ZWQgaW4gaXMgY2FsbGVkIGBmaWx0ZXJgLiBXZSBoYXZlIHRvIHdyaXRlIGEgc2hvcnQgUiBjb21tYW5kIHRvIGNob29zZSB0aGUgcm93cy4gCgplLmcuIGlmIHdlIHdhbnQgb25seSB0aGUgbWFsZSBzYW1wbGVzIHdlIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUuIE5vdGljZSB0aGF0IHR3byAiPSIgc2lnbnMgYXJlIHJlcXVpcmVkLiBJZiB5b3UgdHJ5IGFuZCB1c2UgdGhlIGZ1bmN0aW9uIHdpdGggYSBzaW5nbGUgIj0iIFIgd2lsbCBwcmludCBhIGhlbHBmdWwgaGludC4gCgpgYGB7cn0KZmlsdGVyKGFuYWx5c2lzX2RhdGEsIGdlbmRlciA9PSAiTUFMRSIpCmBgYAphbmQgdGhlIGZlbWFsZXMKCmBgYHtyfQpmaWx0ZXIoYW5hbHlzaXNfZGF0YSwgZ2VuZGVyID09ICJGRU1BTEUiKQpgYGAKCgpBbiBlcXVpdmFsZW50LCBidXQgc29tZXdoYXQgdW5uZWNlc3NhcnkgZm9yIHRoaXMgZXhhbXBsZSwgc3RhdGVtZW50IHdvdWxkIGJlIHRvIGFzayBmb3Igcm93cyB3aGVyZSB0aGUgZ2VuZGVyIGlzICoqbm90IGVxdWFsKiogdG8gYE1BTEVgCgpgYGB7cn0KZmlsdGVyKGFuYWx5c2lzX2RhdGEsIGdlbmRlciAhPSAiTUFMRSIpCmBgYAoKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpJZGVudGlmeWluZyBtYWxlIG9yIGZlbWFsZXMgYmFzZWQgb24gdGhlIGBnZW5kZXJgIGNvbHVtbiBpcyBzaW1wbGlmaWVkIGJ5IHRoZXJlIG9ubHkgYmVpbmcgdHdvIHBvc3NpYmxlIGVudHJpZXMgaW4gdGhpcyBjb2x1bW47IGBNQUxFYCBvciBgRkVNQUxFYC4gSW4gYSByZWFsIGxpZmUgc2l0dWF0aW9uLCBlc3BlY2lhbGx5IGlmIGRhdGEgaGF2ZSBiZWVuIGVudGVyZWQgYnkgaGFuZCwgdGhlcmUgY291bGQgYmUgaW5jb25zaXN0ZW5jaWVzIHRoYXQgcmVxdWlyZSBhY3Rpb24uIFNlZSBhbiBleGFtcGxlIGF0IHRoZSBlbmQgb2YgdGhlIHR1dG9yaWFsLiAKPC9kaXY+CgpXZSBjYW4gYWxzbyByZXN0cmljdCB0aGUgZGF0YSBiYXNlZCBvbiBudW1lcmljIGNvbHVtbnMgYnkgdXNpbmcgYD5gLCBgPGAgZXRjLgoKYGBge3J9CmZpbHRlcihhbmFseXNpc19kYXRhLCBhZ2VfYXRfZGlhZ25vc2lzID4gODApCmBgYAoKCkJ1dCB3aGF0IGlmIHdlIHdhbnQgbWFsZXMgd2l0aCBCcmFpbiB0dW1vdXJzPyBgZHBseXJgIGFsbG93cyB1cyB0byBjb21iaW5lIG1vcmUgdGhhbiBvbmUgY29uZGl0aW9uIGlmIHdlIHNlcGFyYXRlIHRoZW0gd2l0aCBhIGAsYC4gSW4gY29tcHV0aW5nIHRoaXMgaXMga25vd24gYXMgYW4gImFuZCIgc3RhdGVtZW50IGFuZCBvbmx5IHJvd3Mgd2hlcmUgKipib3RoKiogc3RhdGVtZW50cyBhcmUgdHdvIHdpbGwgYmUgc2hvd24uIFRoZSBsaW5lIGNvdWxkIGJlIGV4dGVuZGVkIHRvIGluY2x1ZGUgbW9yZSB0aGFuIHR3byB0ZXN0cyBpZiB3ZSB3YW50ZWQgdG8uIAoKYGBge3J9CmZpbHRlcihhbmFseXNpc19kYXRhLCBnZW5kZXIgPT0gIk1BTEUiLHR1bW9yX3Rpc3N1ZV9zaXRlID09ICJCcmFpbiIpCmBgYAoKCkhvdyBhYm91dCBicmFpbiBvciBsdW5nIHR1bW91cnM/IFVzaW5nIGEgYHxgIHN5bWJvbCBpbnN0ZWFkIG9mIGEgYCxgIGFsbG93cyBmb3IgKmVpdGhlciogb2YgdHdvIChvciBtb3JlKSBjb25kaXRpb25zIHRvIGJlIGBUUlVFYC4gCgoKYGBge3J9CmZpbHRlcihhbmFseXNpc19kYXRhLCB0dW1vcl90aXNzdWVfc2l0ZSA9PSAiQnJhaW4iIHwgdHVtb3JfdGlzc3VlX3NpdGUgPT0gIkx1bmciKQpgYGAKClRvIGFuc3dlciB0aGUgcXVlc3Rpb24gb2YgaG93IG1hbnkgbWFsZXMgLyBmZW1hbGVzIGhhdmUgYSBjZXJ0YWluIHR1bW91ciB0eXBlIHdlIGNvdWxkIG5vdyB1c2UgUiBzdGF0ZW1lbnRzIHN1Y2ggYXM6LQoKYGBge3J9CmZpbHRlcihhbmFseXNpc19kYXRhLCBnZW5kZXIgPT0gIk1BTEUiLHR1bW9yX3Rpc3N1ZV9zaXRlID09ICJCcmFpbiIpCmZpbHRlcihhbmFseXNpc19kYXRhLCBnZW5kZXIgPT0gIkZFTUFMRSIsdHVtb3JfdGlzc3VlX3NpdGUgPT0gIkJyYWluIikKYGBgCgphbmQgbWFrZSBhIG5vdGUgb2YgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW5jbHVkZWQgaW4gdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lLiBIb3dldmVyLCB0aGVyZSBpcyBtdWNoIG1vcmUgZmxleGlibGUgd2F5IG9mIHN1bW1hcmlzaW5nIGRhdGEgaW4gdGhpcyBtYW5uZXIuCgojIyBTdW1tYXJpc2luZwoKQWx0aG91Z2ggdXNlZnVsIGZvciBkYXRhIGV4cGxvcmF0aW9uLCBpdCB3b3VsZCBjbGVhcmx5IGJlIGluZWZmaWNpZW50IHRvIGdldCBnZW5kZXIvdHVtb3VyIHR5cGUgY291bnRzIGluIHRoaXMgd2F5IGFzIHdlIHdvdWxkIGhhdmUgdG8gcmVwZWF0IGZvciBhbGwgY29tYmluYXRpb25zIG9mIHR1bW91ciB0eXBlIGFuZCBnZW5kZXIuIFRoZSBmdW5jdGlvbiBgY291bnRgIGNhbiBub3cgZ2l2ZSB1cyBleGFjdGx5IHdoYXQgd2Ugd2FudC4gVGhlIG91dHB1dCBpcyBnaXZlbiBhcyBhIGB0aWJibGVgLCBzbyB3ZSBjb3VsZCB1c2Ugc29tZSBvZiB0aGUgZnVuY3Rpb25zIHRoYXQgd2UgaGF2ZSBsZWFybnQgYWJvdXQgc28gZmFyIChgc2VsZWN0YCwgYGZpbHRlcmAuLi4pIHRvIGZ1cnRoZXIgbWFuaXB1bGF0ZS4gZS5nLiBvYnRhaW4gdGhlIGNvdW50cyBmb3IganVzdCBCcmFpbi8KCmBgYHtyfQpjb3VudChhbmFseXNpc19kYXRhLCAKICAgICAgdHVtb3JfdGlzc3VlX3NpdGUsCiAgICAgIGdlbmRlcikKYGBgCgpUaGUgYGNvdW50YCBmdW5jdGlvbiBpcyB1c2VmdWwgZm9yIHRhYnVsYXRpbmcgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMsIGJ1dCBmb3Igb3RoZXIgc3VtbWFyeSBzdGF0aXN0aWNzIGEgbW9yZSBnZW5lcmFsIGBzdW1hbXJpc2VgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkLiBUaGlzIGNhbiBiZSB1c2VkIGluIGNvbmp1bmN0aW9uIHdpdGggYmFzaWMgc3VtbWFyeSBmdW5jdGlvbnMgc3VwcG9ydGVkIGJ5IGJhc2UgUi4gQSBzdW1tYXJ5IHN0YXRpc3RpYyBiZWluZyBzb21ldGhpbmcgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBhIHNlcmllcyBvZiBudW1iZXJzIGFuZCBwcm9kdWNlIGEgc2luZ2xlIG51bWJlciBhcyBhIHJlc3VsdC4gZS5nLiB0aGUgYXZlcmFnZSwgbWluaW11bSwgbWF4aW11bSBldGMuCgpgYGB7cn0Kc3VtbWFyaXNlKGFuYWx5c2lzX2RhdGEsIAogICAgICAgICAgQXZlcmFnZSA9IG1lYW4oYWdlX2F0X2RpYWdub3NpcyksCiAgICAgICAgICBtaW4gPSBtaW4oYWdlX2F0X2RpYWdub3NpcyksCiAgICAgICAgICBtYXggPSBtYXgoYWdlX2F0X2RpYWdub3NpcykpCmBgYApIb3dldmVyLCB3ZSBoYXZlIGEgcHJvYmxlbSBkdWUgdG8gbWlzc2luZyB2YWx1ZXMuIElmIFIgc2VlcyBhbmQgbWlzc2luZyB2YWx1ZXMgaW4gYSBjb2x1bW4gaXQgd2lsbCByZXBvcnQgdGhlIG1lYW4sIG1pbmltdW0gb3IgbWF4aW11bSBvZiB0aGF0IGNvbHVtbiBhcyBhIG1pc3NpbmcgdmFsdWUuIEFsdGhvdWdoIHRoaXMgZGVmYXVsdCBiZWhhdmlvdXIgY2FuIGJlIGNoYW5nZWQsIGJlZm9yZSBwcm9jZWVkaW5nIHdlIGNvdWxkIGFsc28gY2hvb3NlIHRvIHJlbW92ZSBhbnkgbWlzc2luZyBvYnNlcnZhdGlvbnMgZnJvbSB0aGUgZGF0YS4gVGhlc2UgYXJlIHJlcHJlc2VudGVkIGJ5IGEgYE5BYCB2YWx1ZSwgd2hpY2ggaXMgYSBzcGVjaWFsIHZhbHVlIGFuZCBub3QgYSBjaGFyYWN0ZXIgbGFiZWwuICAKCmBgYHtyfQpmaWx0ZXIoYW5hbHlzaXNfZGF0YSwgaXMubmEoYWdlX2F0X2RpYWdub3NpcykgfCBpcy5uYSh0dW1vcl90aXNzdWVfc2l0ZSkpCmBgYAoKV2Ugd2FudCB0aGUgb3Bwb3NpdGUgb2YgdGhlIGFib3ZlOyB3aGVyZSB0aGUgYWdlIG9mIGRpYWdub3NpcyBhbmQgdHVtb3VyIHNpdGUgaXMgKipub3QqKiBtaXNzaW5nLiAKCmBgYHtyfQphbmFseXNpc19kYXRhIDwtIGZpbHRlcihhbmFseXNpc19kYXRhLCAhaXMubmEoYWdlX2F0X2RpYWdub3NpcyksICFpcy5uYSh0dW1vcl90aXNzdWVfc2l0ZSkpCmBgYAoKVGhlIHN1bW1hcnkgd2lsbCBub3cgd29yayBhcyBleHBlY3RlZC4KCmBgYHtyfQpzdW1tYXJpc2UoYW5hbHlzaXNfZGF0YSwgCiAgICAgICAgICBBdmVyYWdlID0gbWVhbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1pbiA9IG1pbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1heCA9IG1heChhZ2VfYXRfZGlhZ25vc2lzKSkKYGBgCgpUaGlzIG1pZ2h0IG5vdCBiZSB3aGF0IHdlIHdhbnQgaW4gYWxsIGNpcmN1bXN0YW5jZXMsIGFzIHRoZSBzdGF0aXN0aWNzIGNhbiBhbHNvIGJlIGNhbGN1bGF0ZWQgb24gYSBwZXItdHVtb3VyIHNpdGUgYmFzaXMgdXNpbmcgYGRwbHlyYHMgYGdyb3VwX2J5YCBmdW5jdGlvbi4gCgpgYGB7cn0KCmRhdGFfZ3JvdXBlZCA8LSBncm91cF9ieShhbmFseXNpc19kYXRhLCB0dW1vcl90aXNzdWVfc2l0ZSkKc3VtbWFyaXNlKGRhdGFfZ3JvdXBlZCxBdmVyYWdlID0gbWVhbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1pbiA9IG1pbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1heCA9IG1heChhZ2VfYXRfZGlhZ25vc2lzKSApCmBgYAoKIyMgU29ydGluZyAoYXJyYW5naW5nKQoKV2UgaGF2ZSBwcmV2aW91c2x5IHVzZWQgYGZpbHRlcmAgdG8gcmVzdHJpY3QgdGhlIHJvd3MgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4gUmF0aGVyIHRoYW4ganVzdCBhbmFseXNpbmcgdGhlIG1hbGUgb3IgZmVtYWxlIHBhdGllbnRzIChmb3IgZXhhbXBsZSksIHdlIG1pZ2h0IGFsc28gd2FudCB0aGUgcm93cyBpbiBvdXIgdGFibGUgdG8gYmUgb3JkZXJlZCBhY2NvcmRpbmcgdG8gdGhlIGBnZW5kZXJgIGNvbHVtbi4gCgpgYGB7cn0KYXJyYW5nZShhbmFseXNpc19kYXRhLCBnZW5kZXIpCmBgYApXZSBjYW4gYWxzbyBhcnJhbmdlIGJ5IGNvbHVtbnMgY29udGFpbmluZyBudW1lcmljIHZhbHVlcyBpbiBlaXRoZXIgYXNjZW5kaW5nICh0aGUgZGVmYXVsdCkgb3IgZGVzY2VuZGluZyBvcmRlci4KCmBgYHtyfQphcnJhbmdlKGFuYWx5c2lzX2RhdGEsIGFnZV9hdF9kaWFnbm9zaXMpCiMjIFVzZSBhIGRlc2NlbmRpbmcgb3JkZXIKYXJyYW5nZShhbmFseXNpc19kYXRhLCBkZXNjKGFnZV9hdF9kaWFnbm9zaXMpKQpgYGAKTGlrZSBob3cgc29ydGluZyB3b3JrcyBpbiBFeGNlbCwgd2UgY2FuIGFsc28gdXNlIG11dGxpcGxlIGNvbHVtbnMgZm9yIHNvcnRpbmcuIGUuZy4gaWYgd2Ugd2FudCBvcmRlcmluZyBieSBkaWFnbm9zaXMgYWdlIGZvciBlYWNoIHR1bW91ciB0eXBlIHNlcGFyYXRlbHkuCgpgYGB7cn0KYXJyYW5nZShhbmFseXNpc19kYXRhLCB0dW1vcl90aXNzdWVfc2l0ZSwgYWdlX2F0X2RpYWdub3NpcykKYGBgCgoKIyMgV29ya2Zsb3dzIGFuZCAicGlwaW5nIgoKU28gZmFyIHdlIGhhdmUgdXNlZCBzZXZlcmFsIG9wZXJhdGlvbnMgaW4gaXNvbGF0aW9uLiBIb3dldmVyLCB0aGUgcmVhbCBqb3kgKD8pIG9mIGBkcGx5cmAgaXMgaG93IGRpZmZlcmVudCBvcGVyYXRpb25zIGNhbiBiZSBjaGFpbmVkIHRvZ2V0aGVyLiBMZXRzIHNheSB3ZSBqdXN0IHdhbnRlZCBmZW1hbGUgdHVtb3Vycy4KCmBgYHtyfQpmaWx0ZXIoYW5hbHlzaXNfZGF0YSwgZ2VuZGVyID09ICJGRU1BTEUiKQpgYGAKCgoKT3VyIG5leHQgc3RlcCBjb3VsZCBiZSB0byByZW1vdmUgdGhlIGBnZW5kZXJgIGNvbHVtbiBzaW5jZSBpdCBpcyBzb21ld2hhdCByZWR1bmRhbnQuCgpgYGB7cn0KYW5hbHlzaXNfZGF0YTIgPC0gZmlsdGVyKGFuYWx5c2lzX2RhdGEsIGdlbmRlciA9PSAiRkVNQUxFIikKc2VsZWN0KGFuYWx5c2lzX2RhdGEyLCB0dW1vcl90aXNzdWVfc2l0ZSwgYWdlX2F0X2RpYWdub3NpcykKIyMgb3IgCiMjIHNlbGVjdChhbmFseXNpc19kYXRhMiwgLWdlbmRlcikKYGBgCgpUaGUgY29kZSB3b3VsZCBxdWlja2x5IGdldCBjdW1iZXJzb21lIGlmIHdlIHdhbnRlZCB0byBpbmNsdWRlIGFkZGl0aW9uYWwgc3RlcHMgc3VjaCBhcyByZW1vdmluZyBgTkFgIHZhbHVlcy4gQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggY2FsbGVkICJwaXBpbmciIGlzIHJlY29tbWVuZGVkIGFuZCBhY3RpdmF0ZWQgYnkgYWRkaW5nIGAlPiVgIGF0IHRoZSBlbmQgb2YgYSBsaW5lLiBUaGlzIHRlbGxzIFIgdG8gdXNlIHRoZSBvdXRwdXQgb2YgdGhlIGN1cnJlbnQgbGluZSBhcyB0aGUgZmlyc3QgYXJndW1lbnQgb24gdGhlIG5leHQgbGluZS4gSW4gdGhpcyBjdXJyZW50IGV4YW1wbGUgaXQgbWVhbnMgd2UgZG9uJ3QgbmVlZCB0byBzcGVjaWZ5IHdoaWNoIGRhdGEgZnJhbWUgdGhhdCBgc2VsZWN0YCB1c2VzIGFzIGlucHV0IC0gaXQgd2lsbCB1c2UgdGhlIGRhdGEgZnJhbWUgY3JlYXRlZCBieSB0aGUgYGZpbHRlcmAgaW4gdGhlIHByZXZpb3VzIGxpbmUuIFRoZSBjb2RlIHdyaXR0ZW4gdXNpbmcgYCU+JWAgaXMgbW9yZSBjb25jaXNlLgoKCmBgYHtyfQpmaWx0ZXIoYW5hbHlzaXNfZGF0YSwgZ2VuZGVyID09ICJGRU1BTEUiKSAlPiUgIyMgYW5kIHRoZW4uLi4KICBzZWxlY3QodHVtb3JfdGlzc3VlX3NpdGUsIGFnZV9hdF9kaWFnbm9zaXMpICMjICU+JSBhbmQgdGhlbi4uLgpgYGAKCjxkaXYgY2xhc3M9ImluZm9ybWF0aW9uIj4KVGhlIGAlPiVgIG9wZXJhdGlvbiBiZWNvbWVzIGF2YWlsYWJsZSB3aGVuIHlvdSBsb2FkIGBkcGx5cmAuIElmIHlvdSB3aXNoIHRvIHVzZSBwaXBpbmcgb3V0c2lkZSBvZiBgZHBseXJgIHRoZXJlIGlzIGFsc28gYSAiYmFzZSIgZXF1aXZhbGVudCBgfD5gIHRoYXQgZG9lc24ndCByZXF1aXJlIGFueSBsaWJyYXJpZXMgdG8gYmUgbG9hZGVkCmBgYHtyfQpmaWx0ZXIoYW5hbHlzaXNfZGF0YSwgZ2VuZGVyID09ICJGRU1BTEUiKSB8PiAjIyBhbmQgdGhlbi4uLgogIHNlbGVjdCh0dW1vcl90aXNzdWVfc2l0ZSwgYWdlX2F0X2RpYWdub3NpcykgIyMgfD4gYW5kIHRoZW4uLi4KYGBgCjwvZGl2PgoKV2UgcmVjZW50bHkgY3JlYXRlZCBhIHN1bW1hcnkgdGFibGUgZm9yIGVhY2ggdHVtb3VyIHR5cGUgZ2l2aW5nIHRoZSBhdmVyYWdlLCBtaW5pbXVtIGFuZCBtYXhpbXVtIG9mIGRpYWdub3NpcyBhZ2UuIFRoaXMgY2FuIGJlIHJlcGxpY2F0ZWQgdXNpbmcgYCU+JWAgYW5kIGFuIGV4dHJhIHNvcnRpbmcgc3RlcCBhZGRlZCB0byB0aGUgZW5kLgoKYGBge3J9Cmdyb3VwX2J5KGFuYWx5c2lzX2RhdGEsIHR1bW9yX3Rpc3N1ZV9zaXRlKSAlPiUgCnN1bW1hcmlzZShBdmVyYWdlID0gbWVhbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1pbiA9IG1pbihhZ2VfYXRfZGlhZ25vc2lzKSwKICAgICAgICAgIG1heCA9IG1heChhZ2VfYXRfZGlhZ25vc2lzKSkgJT4lIAogIGFycmFuZ2UoQXZlcmFnZSkKYGBgCgoKIyMgT3ZlcnZpZXcgb2YgcGxvdHRpbmcKCk91ciByZWNvbW1lbmRpbmcgd2F5IG9mIGNyZWF0aW5nIHBsb3RzIGluIFJTdHVkaW8gaXMgdG8gdXNlIHRoZSBgZ2dwbG90MmAgcGFja2FnZSAtIGVzcGVjaWFsbHkgYXMgaXQgaW50ZXJhY3RzIHdlbGwgd2l0aCBgZHBseXJgIGFuZCBvdGhlciBgdGlkeXZlcnNlYCBwYWNrYWdlcy4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmBgYAoKQSBjb3VwbGUgb2YgdXNlZnVsIHJlZmVyZW5jZXMgYXJlIGdpdmVuIGhlcmUtCgotIFtnZ3Bsb3QyIHJlZmVyZW5jZSBndWlkZV0oaHR0cHM6Ly9wb3NpdC5jby93cC1jb250ZW50L3VwbG9hZHMvMjAyMi8xMC9kYXRhLXZpc3VhbGl6YXRpb24tMS5wZGYpCi0gW0Zsb3djaGFydCBmb3IgZGVjaWRpbmcgb24gd2hhdCBncmFwaCB0eXBlIHRvIHVzZV0oaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tLykKCgpUaGUgZ2VuZXJhbCBwcmluY2lwbGUgb2YgY3JlYXRpbmcgYSBwbG90IGlzIHRoZSBzYW1lIHJlZ2FyZGxlc3Mgb2Ygd2hhdCBraW5kIG9mIHBsb3Qgd2Ugd2FudCB0byBtYWtlCgotIHNwZWNpZnkgdGhlIGBkYXRhIGZyYW1lYCBjb250YWluaW5nIHRoZSBkYXRhIHdlIHdhbnQgdG8gcGxvdAotIHNwZWNpZnkgd2hpY2ggY29sdW1ucyBpbiB0aGF0IGRhdGEgZnJhbWUgd2Ugd2FudCB0byB1c2UgZm9yIHZhcmlvdXMgYWVzdGhldGljIGFzcGVjdHMgb2YgdGhlIHBsb3QKLSBkZWZpbmUgdGhlIHR5cGUgb2YgcGxvdCB3ZSB3YW50Ci0gYXBwbHkgYW55IGFkZGl0aW9uYWwgZm9ybWF0IGNoYW5nZXMKCkEgYmFyIHBsb3Qgd291bGQgYmUgYSBuYXR1cmFsIGNob2ljZSBmb3Igc2hvd2luZyB0aGUgY291bnRzIG9mIG1hbGUgLyBmZW1hbGUgc2FtcGxlcy4gVGhlIGBnZW9tX2JhcmAgcGxvdCB3aWxsIGF1dG9tYXRpY2FsbHkgY291bnQgaG93IG1hbnkgb2NjdXJyZW5jZXMgdGhlcmUgYXJlIGZvciBlYWNoIHZhbHVlLgoKYGBge3J9CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGdlbmRlcikpICsgZ2VvbV9iYXIoKQpgYGAKCk51bWVyaWNhbCBkYXRhIGNhbiBiZSB2aXN1YWxpc2VkIHVzaW5nIGEgZGVuc2l0eSBwbG90IG9yIGhpc3RvZ3JhbS4gVGhlIGRlbnNpdHkgaXMgYXV0b21hdGljYWxseSBjYWxjdWxhdGVkIGFuZCBkaXNwbGF5ZWQgb24gdGhlIHktYXhpcy4KCmBgYHtyfQpnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSBhZ2VfYXRfZGlhZ25vc2lzKSkgKyBnZW9tX2RlbnNpdHkoKQpgYGAKCgpJbiBvcmRlciB0byBjb21wYXJlIHRoZSBhZ2UgZGlzdHJpYnV0aW9ucyBvZiBkaWZmZXJlbnQgdHVtb3VyIHR5cGVzIHdlIGNhbiBhbHNvIGltYWdpbmUgdGhpcyBiZWluZyBkaXNwbGF5ZWQgYXMgYSBzZXJpZXMgb2YgYm94cGxvdHMgd2l0aAoKLSB0aGUgYWdlIHZhcmlhYmxlIG9uIHRoZSB5LWF4aXMKLSB0aGUgdHlwZSBvZiB0dW1vdXIgb24gdGhlIHgtYXhpcwoKdGhpcyBjYW4gYmUgdHJhbnNsYXRlZCBpbnRvIGBnZ3Bsb3QyYCBsYW5ndWFnZSBhcyBmb2xsb3dzIC0KCmBgYHtyfQpnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSB0dW1vcl90aXNzdWVfc2l0ZSwgeSA9IGFnZV9hdF9kaWFnbm9zaXMpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKQSBkaXNhZHZhbnRhZ2Ugb2YgdGhlIGJveHBsb3QgaXMgdGhhdCBpdCBvbmx5IGdpdmVzIGEgdmVyeSBjcnVkZSBzdW1tYXJ5IG9mIHRoZSBkYXRhLiBJdCBjYW4gYmUgbWlzbGVhZGluZyB3aGVuIGFwcGxpZWQgdG8gZGF0YSB3aXRoIGZldyBvYnNlcnZhdGlvbnMgYW5kIGlzIG9mdGVuIHByZWZlcmFibGUgdG8gYWRkIGluZGl2aWR1YWwgZGF0YSBwb2ludHMKCmBgYHtyfQpnZ3Bsb3QoYW5hbHlzaXNfZGF0YSwgYWVzKHggPSB0dW1vcl90aXNzdWVfc2l0ZSwgeSA9IGFnZV9hdF9kaWFnbm9zaXMpKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKQpgYGAKQWRkaW5nIHNvbWUgY29sb3VyIHRvIHRoZSBwbG90IGNhbiBiZSBhY2hpZXZlZCBieSBhZGRpbmcgYSBgZmlsbGAgYWVzdGhldGljIGFuZCBzcGVjaWZ5aW5nIHdoYXQgY29sdW1uIHRvIG1hcCB0aGUgY29sb3VycyB0b28uIEEgY29sb3VyIHBhbGV0dGUgaXMgYXV0b21hdGljYWxseSBjaG9zZW4sIGJ1dCBjYW4gYmUgY2hhbmdlZCBhZnRlcndhcmRzIGlmIHdlIHdpc2guCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID0gdHVtb3JfdGlzc3VlX3NpdGUsIHkgPSBhZ2VfYXRfZGlhZ25vc2lzLCBmaWxsID0gdHVtb3JfdGlzc3VlX3NpdGUpKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKQpgYGAKQWRkaW5nIHRoZSBgZmlsbGAgYWVzdGhldGljIGZvciB0aGUgZGVuc2l0eSBwbG90IGNhbiBiZSB1c2VkIHRvIHNob3cgYSBzZXBhcmF0ZSBjdXJ2ZSBmb3IgZWFjaCB0dW1vdXIgdHlwZS4gCgpgYGB7cn0KIyMgYWxwaGEgb2YgMC41IHVzZWQgdG8gbWFrZSB0aGUgY3VydmVzIHRyYW5zcGFyZW50CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGFnZV9hdF9kaWFnbm9zaXMsIGZpbGwgPSB0dW1vcl90aXNzdWVfc2l0ZSkpICsgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNSkKYGBgCkFub3RoZXIgdXNlZnVsIHRlY2huaXF1ZSBmb3Igc3BsaXR0aW5nIHRoZSBwbG90cyBiYXNlZCBvbiBhIHZhcmlhYmxlIGlzIHRvIHVzZSB0aGUgYGZhY2V0X3dyYXBgIGZ1bmN0aW9uIHRoYXQgd2lsbCBnaXZlIGEgZ3JpZCBvZiBwbG90cy4gRm9yIGluc3RhbmNlIHdlIGNhbiBzaG93IG1hbGUvZmVtYWxlIGNvdW50cyBmb3IgZWFjaCB0dW1vdXIgdHlwZSBzZXBhcmF0ZWx5LgoKYGBge3J9CmdncGxvdChhbmFseXNpc19kYXRhLCBhZXMoeCA9IGdlbmRlcixmaWxsPWdlbmRlcikpICsgZ2VvbV9iYXIoKSArIGZhY2V0X3dyYXAofnR1bW9yX3Rpc3N1ZV9zaXRlKQpgYGAKCkJ5IGNvbWJpbmluZyBhbGwgdGhlIHRlY2huaXF1ZXMgd2UgaGF2ZSBzZWVuIHdlIGNhbiBjb21wYXJlIHRoZSBkaWFnbm9zaXMgYWdlIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXM7IHNlcGFyYXRlbHkgZm9yIGVhY2ggdHVtb3VyIHR5cGUuCgpgYGB7cn0KZ2dwbG90KGFuYWx5c2lzX2RhdGEsIGFlcyh4ID1nZW5kZXIsIHkgPSBhZ2VfYXRfZGlhZ25vc2lzLCBmaWxsID0gZ2VuZGVyKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHdpZHRoPTAuMSkgKyBmYWNldF93cmFwKH50dW1vcl90aXNzdWVfc2l0ZSkKYGBgCgojIENoYWxsZW5nZXMgb2YgIm1lc3N5IiBkYXRhCgpSZWFsLWxpZmUgZGF0YSBhcmUgb2Z0ZW4gbGVzcyBzdHJhaWdodGZvcndhcmQgdG8gZGVhbCB3aXRoIHRoYW4gdGhlICJjbGVhbmVkIiBkYXRhc2V0IHByZXNlbnRlZCBoZXJlLiBEZXNwaXRlIHRoZSBtYW55IGhpZ2gtdGhyb3VnaHB1dCB0ZWNobm9sb2dpZXMgdGhhdCBhcmUgdXNlZCBmb3Igc2NpZW50aWZpYyBpbnZlc3RpZ2F0aW9uLCB0aGVyZSBpcyBpbmV2aXRhYmx5IGEgc3ByZWFkc2hlZXQocykgbmVlZGVkIHRvIGRlc2NyaWJlIHRoZSBleHBlcmltZW50YWwgc2V0dXAgYW5kIHRoaXMgaXMgdHlwaWNhbGx5IGVudGVyZWQgbWFudWFsbHkuCgpTby1jYWxsZWQgIkRhdGEgV3JhbmdsaW5nIiBpcyBhIGNydWNpYWwgYW5kIHRpbWUtY29uc3VtaW5nIHBhcnQgb2YgdGhlIGFuYWx5c2lzIHByb2Nlc3MgdGFraW5nIDgwJSBvZiBhbmFseXNpcyB0aW1lIGJ5IHNvbWUgZXN0aW1hdGVzLiBIYWRsZXkgV2lja2hhbSwgQ2hpZWYgU2NpZW50aXN0IGF0IFBvc2l0IGFuZCBsZWFkIGF1dGhvciBvZiBgZ2dwbG90MmAgbGlrZW5zIHRpZHkgYW5kIG1lc3N5IGRhdGEgdG8gTGVvIFRvbHN0b3kncyBxdW90ZSBhYm91dCBmYW1pbGllczotCgo+IEhhcHB5IGZhbWlsaWVzIGFyZSBhbGwgYWxpa2U7IGV2ZXJ5IHVuaGFwcHkgZmFtaWx5IGlzIHVuaGFwcHkgaW4gaXRzIG93bgp3YXkKCgo+IExpa2UgZmFtaWxpZXMsIHRpZHkgZGF0YXNldHMgYXJlIGFsbCBhbGlrZSBidXQgZXZlcnkgbWVzc3kgZGF0YXNldCBpcyBtZXNzeSBpbiBpdHMgb3duIHdheS4gCgpBIGNvbXByZWhlbnNpdmUgZ3VpZGUgdG8gdGhlIGlzc3VlcyBzdXJyb3VuZGluZyBkYXRhIGVudHJ5IHZpYSBzcHJlYWRzaGVldHMsIGFuZCBob3cgdG8gYXZvaWQgdGhlbSwgaXMgZ2l2ZW4gYnkgRGF0YSBDYXJwZW50cnkuCgotIFtEYXRhIENhcnBlbnRyeSBTcHJlYWRzaGVldHMgbGVzc29uXShodHRwczovL2RhdGEtbGVzc29ucy5naXRodWIuaW8vZ2FwbWluZGVyLXNwcmVhZHNoZWV0LykKCkhvd2V2ZXIsIGZvciBwdWJsaWMgZGF0YSB0aGF0IHdlIGhhdmUgbm8gY29udHJvbCBvdmVyIHdlIG9mdGVuIGhhdmUgbm8gY2hvaWNlIGJ1dCB0byBjbGVhbiB0aGUgZGF0YSBvdXJzZWx2ZXMuIFdlIGhhdmUgaW50ZW50aW9uYWxseSBjcmVhdGVkIGFuIGFsdGVybmF0aXZlIGRhdGFzZXQgd2l0aCBhIGZldyBpbnRlbnRpb25hbCBpc3N1ZXMgdG8gaWxsdXN0cmF0ZSB0aGUgY2xlYW5pbmcgcHJvY2Vzcy4KCmBgYHtyfQptZXNzeSA8LSByZWFkX3RzdigidGNnYV9jbGluaWNhbF9NRVNTWS50c3YiKQptZXNzeQpgYGAKCiMjIFdoaXRlc3BhY2UKCiJ3aGl0ZXNwYWNlIiBpcyB0aGUgYWRkaXRpb24gb2YgYSBibGFuayBjaGFyYWN0ZXIgb3Igc3BhY2UgdG8gdGhlIGJlZ2lubmluZyBvciBlbmQgb2YgdGV4dC4gVHJhZGl0aW9uYWxseSBpdCBpcyBhIHByb2JsZW0gYmVjYXVzZSBpdCB3aWxsIGNyZWF0ZSBleHRyYSBjYXRlZ29yaWVzIGluIHlvdXIgZGF0YS4gZS5nLiBgTUFMRWAgYW5kIGBNQUxFIGAuIFRoZSBtZXNzeSBkYXRhc2V0IHRoYXQgeW91IGhhdmUganVzdCBpbXBvcnRlZCBpbmNsdWRlcyBzb21lIHdoaXRlc3BhY2UgaW4gdGhlIGB0dW1vcl90aXNzdWVfc2l0ZWAgY29sdW1uLiBIb3dldmVyLCB0aGUgYHJlYWRfdHN2YCBmdW5jdGlvbiBhdXRvbWF0aWNhbGx5IGlnbm9yZXMgd2hpdGVzcGFjZSB2YWx1ZXMgYXMgdGhlIGB0cmltX3dzYCBhcmd1bWVudCBvZiBgcmVhZF90c3ZgIGlzIHNldCB0byBgVFJVRWAgKHNlZSB0aGUgaGVscCBwYWdlIGA/cmVhZF90c3ZgKS4gCgpgYGB7cn0KbWVzc3lfd3MgPC0gcmVhZF90c3YoInRjZ2FfY2xpbmljYWxfTUVTU1kudHN2IiwgCiAgICAgICAgICAgICAgICAgICAgIHRyaW1fd3MgPSBGQUxTRSkKbWVzc3lfd3MKY291bnQobWVzc3lfd3MsdHVtb3JfdGlzc3VlX3NpdGUpCmBgYAoKVGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIG5vdyBjb250YWlucyB0d28gYXBwYXJlbnRseSBpZGVudGljYWwgY2F0ZWdvcmllcyBmb3IgYEJsYWRkZXJgLiBIb3dldmVyLCB3aXRoIHRoZSB1c2Ugb2YgdGhlIGBuY2hhcmAgZnVuY3Rpb24sIHdoaWNoIGNvdW50cyB0aGUgbnVtYmVyIG9mIGNoYXJhY3RlcnMsIHdlIGNhbiBzZWUgdGhhdCBleHRyYSBzcGFjZXMgbXVzdCBiZSBpbmNsdWRlZC4KCmBgYHtyfQpjb3VudChtZXNzeV93cyx0dW1vcl90aXNzdWVfc2l0ZSkgJT4lIAogIG11dGF0ZSgiTGVuZ3RoX29mX0xhYmVsIiA9IG5jaGFyKHR1bW9yX3Rpc3N1ZV9zaXRlKSkKYGBgCgpDbGVhcmx5IHdlIGNvdWxkIGhhdmUgdXNlZCB0aGUgZGVmYXVsdCBzZXR0aW5ncyBmb3IgYHJlYWRfdHN2YCBhbmQgdGhlIHByb2JsZW0gd291bGQgbm90IGhhdmUgb2NjdXJyZWQuIE90aGVyd2lzZSwgaXQgaXMgdXNlZnVsIHRvIGtub3cgYWJvdXQgdGhlIGBzdHJpbmdyYCBwYWNrYWdlIHRoYXQgY29udGFpbnMgbWFueSB1c2VmdWwgZnVuY3Rpb25zIGZvciBjbGVhbmluZyBjaGFyYWN0ZXIgZGF0YS4KCi0gW1RoZSBzdHJpbmdyIHBhY2thZ2VdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnLykKCkZvciB0aGUgZXhhbXBsZSBvZiByZW1vdmluZyB3aGl0ZXNwYWNlIHdlIGNhbiB1c2UgdGhlIGBzdHJfdHJpbWAgZnVuY3Rpb24gY29tYmluZWQgd2l0aCBhIGBtdXRhdGVgLiBUaGlzIHdpbGwgcmVwbGFjZSBhbGwgdGhlIHdoaXRlc3BhY2UgaW4gdGhlIGB0dW1vcl90aXNzdWVfc2l0ZWAgY29sdW1uIGFuZCBvdmVyd3JpdGUgdGhlIGNvbHVtbi4gSWYgd2UgcmVwZWF0IGEgY291bnQgYWZ0ZXJ3YXJkcyB3ZSBzZWUgb25seSB0aGUgdW5pcXVlIGVudHJpZXMgdGhhdCB3ZSBleHBlY3QuCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQptdXRhdGUobWVzc3lfd3MsIHR1bW9yX3Rpc3N1ZV9zaXRlID0gc3RyX3RyaW0odHVtb3JfdGlzc3VlX3NpdGUpKSAlPiUKICBjb3VudCh0dW1vcl90aXNzdWVfc2l0ZSkKYGBgCgojIyBJbmNvbnNpc3RlbnQgY29kaW5nIG9mIHZhcmlhYmxlcwoKVW5mb3J0dW5hdGVseSB0aGUgYHR1bW9yX3Rpc3N1ZV9zaXRlYCBjb2x1bW4gaXMgbm90IHRoZSBvbmx5IG9uZSB3aXRoIGlzc3VlIHRoYXQgbmVlZCBmaXhpbmcgd2l0aCB0aGVzZSBkYXRhLiBJZiwgYXMgYmVmb3JlLCB3ZSB0cnkgYW5kIHBsb3QgdGhlIG51bWJlciBvZiBtYWxlcy9mZW1hbGVzIGluIHRoZSBkYXRhc2V0IHdlIGdldCBhIHN1cnByaXNlLgoKYGBge3J9CmdncGxvdChtZXNzeSwgYWVzKHggPSBnZW5kZXIpKSArIGdlb21fYmFyKCkKYGBgCgpUaGVyZSBpcyBubyBkaWZmZXJlbnRpYXRpb24gYmV0d2VlbiBgZmVtYWxlYCBhbmQgYEZFTUFMRWAgb3IgYG1hbGVgIGFuZCBgTUFMRWAuIFdoaWxzdCB3ZSBjYW4gaW50dWl0aXZlbHkgZGVjaWRlIHRoYXQgdGhlc2UgcmVwcmVzZW50IHRoZSBzYW1lIHZhbHVlLCB0aGV5IGRvIG5vdCBnZXQgYXV0b21hdGljYWxseSBjb21iaW5lZCBpbiBSLiBUaGUgY29uc2VxdWVuY2UgYmVpbmcgdGhhdCBhdHRlbXB0cyB0byBpZGVudGlmeSBhbGwgdGhlIG1hbGUgcGF0aWVudHMgd2lsbCByZXF1aXJlIHNvbWUgY2FyZWZ1bCBjb2RpbmcuIFRoZSBleGFtcGxlIHVzZWQgcHJldmlvdXNseSB3aWxsIG5vdyBubyBsb25nZXIgaWRlbnRpZnkgYWxsIHRoZSBjb3JyZWN0IHBhdGllbnRzLgoKYGBge3J9CmZpbHRlcihtZXNzeSwgZ2VuZGVyID09ICJNQUxFIikKYGBgCk9uZSBzb2x1dGlvbiB3aGVuIGZpbHRlcmluZyB3b3VsZCBiZSB0byBhZGQgZGlmZmVyZW50IGNyaXRlcmlhIHRvIGFjY291bnQgZm9yIHRoZSBkaWZmZXJlbnQgY2FwaXRhbGlzYXRpb24KCmBgYHtyfQpmaWx0ZXIobWVzc3ksIGdlbmRlciA9PSAiTUFMRSIgfCBnZW5kZXIgPT0gIm1hbGUiKQpgYGAKSG93ZXZlciwgc2luY2Ugd2Uga25vdyB0aGF0IHRoZSBlcnJvciBpcyBkdWUgdG8gaW5jb25zaXN0ZW50IHVzZSBvZiB1cHBlci9sb3dlcmNhc2Ugd2UgY2FuIHVzZSB0aGUgYHN0cl90b191cHBlcmAgZnVuY3Rpb24gaW4gYHN0cmluZ3JgIHRvIGNvbnZlcnQgYWxsIHZhbHVlcyB0byB1cHBlcmNhc2UuIE9yIGluZGVlZCB3ZSBjb3VsZCBjb252ZXJ0IGFsbCB0byBsb3dlcmNhc2UgdXNpbmcgYHN0cl90b19sb3dlcmAgaWYgd2UgcHJlZmVyZWQuCgpgYGB7cn0KbWVzc3kgJT4lIAogIG11dGF0ZShnZW5kZXIgPSBzdHJfdG9fdXBwZXIoZ2VuZGVyKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGdlbmRlcikpICsgZ2VvbV9iYXIoKQpgYGAKV2Ugd291bGQgcHJvYmFibHkgd2FudCB0byBhbHNvIG1ha2UgdGhlIGNoYW5nZSBwZXJtYW5lbnQgYnkgY3JlYXRpbmcgYSBuZXcgdmFyaWFibGUKCmBgYHtyfQpjbGVhbmVkIDwtIHJlYWRfdHN2KCJ0Y2dhX2NsaW5pY2FsX01FU1NZLnRzdiIpICU+JSAKICAgIG11dGF0ZShnZW5kZXIgPSBzdHJfdG9fdXBwZXIoZ2VuZGVyKSkgCmBgYAoKQSBtb3JlIGdlbmVyaWMgYXBwcm9hY2ggd291bGQgYmUgdG8gdXNlIHRoZSBgZm9yY2F0c2AgcGFja2FnZSB0byByZXBsYWNlIGFsbCBvY2N1cnJlbmNlcyBvZiBgbWFsZWAgd2l0aCBgTUFMRWAgYW5kIHRoZSBzYW1lIGZvciBmZW1hbGVzLgoKLSBbVGhlIGZvcmNhdHMgcGFja2FnZV0oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcvKQoKVGhlIHBhY2thZ2UgYWxsb3dzIHVzIHRvICJyZWNvZGUiIGVudHJpZXMgaW4gYSBjb2x1bW4gdGhhdCBjb250YWlucyBhIGBmYWN0b3JgLiBpLmUuIGNhdGVnb3JpY2FsLgoKYGBge3J9CmxpYnJhcnkoZm9yY2F0cykKbXV0YXRlKG1lc3N5LCBnZW5kZXIgPSBmb3JjYXRzOjpmY3RfcmVjb2RlKGdlbmRlciwiTUFMRSI9Im1hbGUiKSwKICAgICAgIGdlbmRlciA9IGZvcmNhdHM6OmZjdF9yZWNvZGUoZ2VuZGVyLCJGRU1BTEUiPSJmZW1hbGUiKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZGVyKSkgKyBnZW9tX2JhcigpCmBgYAoKVGhlIGFwcHJvYWNoIGlzIG1vcmUgZmxleGlibGUgdGhhbiBtZXJlbHkgY2hhbmdpbmcgdGhlIGNhc2UsIGFzIGl0IGNvdWxkIGFsc28gcmVwbGFjZSBvdGhlciB2YWx1ZXMgc3VjaCBhcyAibSIgb3IgImYiIGlmIHRoZXkgZXhpc3RlZC4KCmBgYHtyIGV2YWw9RkFMU0V9CiMjIEp1c3QgZXhhbXBsZSBjb2RlIGlmIHdlIHdhbnRlZCB0byByZXBsYWNlICJtIiB3aXRoICJNQUxFIgptZXNzeSA8LSBtZXNzeSAlPiUKICBtdXRhdGUoZ2VuZGVyID0gZmN0X3JlY29kZShnZW5kZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUFMRSIgPSAibWFsZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1BTEUiID0gIm0iIAogICkpCgoKYGBgCgoKIyMgRGlmZmVyZW50IG1lYW5zIG9mIHJlcHJlc2VudGluZyBtaXNzaW5nIHZhbHVlcwoKVGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHN0cmF0ZWdpZXMgZm9yIHJlcHJlc2VudGluZyBtaXNzaW5nIGRhdGEuIFRoZSBgcmVhZF90c3ZgIGZ1bmN0aW9uIHNob3VsZCBhdXRvbWF0aWNhbGx5IGRldGVjdCBhbnkgYE5BYCB2YWx1ZXMgaW4gdGhlIHNvdXJjZSBkYXRhc2V0IGFuZCB0cmVhdCB0aGVtIGFwcHJvcHJpYXRlbHkuIEhvd2V2ZXIsIGluIG91ciBtZXNzeSBkYXRhc2V0IHdlIGFsc28gaGF2ZSBgTlVMTGAgdmFsdWVzIChhcyBzZWVuIGJ5IG1ha2luZyBhIGNvdW50IG9mIHRoZSB2YWx1ZXMgaW4gYGFnZV9hdF9kaWFnbm9zaXNgKS4gCgpgYGB7cn0KY291bnQobWVzc3ksIGFnZV9hdF9kaWFnbm9zaXMpICU+JSBhcnJhbmdlKGRlc2MobikpCmBgYAoKQmVjYXVzZSB0aGUgYE5VTExgIHZhbHVlIGlzIHByZXNlbnQgaW4gdGhlIGBhZ2VfYXRfZGlhZ25vc2lzYCBjb2x1bW4sIFIgd2lsbCB0cmVhdC4gdGhlIGVudGlyZSBjb2x1bW4gYXMgY29udGFpbmluZyBjaGFyYWN0ZXJzLiBUaGVyZWZvcmUgd2UgY2Fubm90IHVzZSB0aGUga2luZCBvZiBwbG90cyB3ZSB3b3VsZCBleHBlY3Qgd2l0aCBudW1lcmljIGRhdGEKCmBgYHtyIGV2YWw9RkFMU0V9CmdncGxvdChtZXNzeSwgYWVzKHggPSBhZ2VfYXRfZGlhZ25vc2lzKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKTGlrZXdpc2Ugd2UgY2FuJ3QgY2FsY3VsYXRlIG51bWVyaWMgc3VtbWFyaWVzOyBhbHRob3VnaCBSIHdpbGwgYXR0ZW1wdCB0byBhbmQgY3JlYXRlIGEgZGF0YSBmcmFtZSBvZiBgTkFgIHZhbHVlcyByYXRoZXIgdGhhbiBnaXZpbmcgYW4gZXJyb3IuCgpgYGB7cn0KICBncm91cF9ieShtZXNzeSwgdHVtb3JfdGlzc3VlX3NpdGUpICU+JSAKICBzdW1tYXJpc2UoTWVhbl9EaWFnbm9zaXNfQWdlID0gbWVhbihhZ2VfYXRfZGlhZ25vc2lzLG5hLnJtPVRSVUUpKQpgYGAKClRoZSBgcmVhZF90c3ZgIGZ1bmN0aW9uIGhhcyB0aGUgYWJpbGl0eSB0byByZXBsYWNlIE5BIHZhbHVlcyB3aGVuIHRoZSBkYXRhIGFyZSBpbXBvcnRlZC4gU3BlY2lmaWNhbGx5LCB0aGUgYG5hYCBhcmd1bWVudCBjYW4gYmUgdXNlZCB0byBkZWZpbmUgd2hhdCB2YWx1ZXMgYXJlIGJlaW5nIHVzZWQgdG8gcmVwcmVzZW50IG1pc3NpbmcuCgpgYGB7cn0KcmVhZF90c3YoInRjZ2FfY2xpbmljYWxfTUVTU1kudHN2IiwgbmEgPSBjKCJOVUxMIiwiTkEiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGFnZV9hdF9kaWFnbm9zaXMpKSArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpgYGB7cn0KcmVhZF90c3YoInRjZ2FfY2xpbmljYWxfTUVTU1kudHN2IiwgbmEgPSBjKCJOVUxMIiwiTkEiKSkgJT4lIAogIGdyb3VwX2J5KHR1bW9yX3Rpc3N1ZV9zaXRlKSAlPiUgCiAgc3VtbWFyaXNlKE1lYW5fRGlhZ25vc2lzX0FnZSA9IG1lYW4oYWdlX2F0X2RpYWdub3NpcyxuYS5ybT1UUlVFKSkKYGBgCgojIyBJbmNsdWRpbmcgdW5pdHMgaW4gdGhlIGNvbHVtbgoKVGhlIGZpbmFsIGNvbHVtbiBpbiB0aGlzIGV4YW1wbGUgY29udGFpbnMgaGVpZ2h0IGluZm9ybWF0aW9uICh3aGVyZSBhdmFpbGFibGUpIGZvciBvdXIgcGF0aWVudHMuIENsZWFybHkgaXQgaXMgaW1wb3J0YW50IHRvIGtub3cgd2hhdCB1bml0cyB0aGlzIGlzIHJlY29yZGVkIGluLCBidXQgcGxhY2luZyB0aGUgdW5pdHMgaW5zaWRlIHRoZSBlbnRyaWVzIGNyZWF0ZXMgaXNzdWVzIGFzIHdlIGNhbid0IHRyZWF0IHRoZSBkYXRhIGFzIG51bWJlcnMuCgpgYGB7cn0KYXJyYW5nZShtZXNzeSxoZWlnaHRfYXRfZGlhZ25vc2lzKQpgYGAKVGhlIGBzdHJpbmdyYCBwYWNrYWdlIGNhbiBiZSB1c2VkIGFnYWluLCBhbmQgdGhpcyB0aW1lIGEgZnVuY3Rpb24gY2FsbGVkIGBzdHJfcmVtb3ZlX2FsbGAgd2hpY2ggcmVtb3ZlcyBhbGwgb2NjdXJyZW5jZXMgb2YgYSBwYXJ0aWN1bGFyIHN0cmluZy4gSW4gcGFydGljdWxhciB3ZSB3YW50IHRvIHJlbW92ZSBgY21gLiBXZSB3aWxsIG5lZWQgYW4gYWRkaXRpb25hbCBzdGVwIHRvIGNvbnZlcnQgdGhlIGNvbHVtbiBpbnRvIG51bWVyaWMgdmFsdWVzCgpgYGB7cn0KbWVzc3kgJT4lIAogIG11dGF0ZShoZWlnaHRfYXRfZGlhZ25vc2lzPXN0cl9yZW1vdmVfYWxsKGhlaWdodF9hdF9kaWFnbm9zaXMsICJjbSIpKSAlPiUgCiAgbXV0YXRlKGhlaWdodF9hdF9kaWFnbm9zaXMgPSBhcy5udW1lcmljKGhlaWdodF9hdF9kaWFnbm9zaXMpKSAlPiUgCiAgYXJyYW5nZShoZWlnaHRfYXRfZGlhZ25vc2lzKQpgYGAKClRoZXJlIGlzIHVzdWFsbHkgbW9yZSB0aGFuIG9uZSB3YXkgb2YgY29tcGxldGluZyBhIHRhc2sgaW4gUi4gSW4gdGhpcyBpbnN0YW5jZSwgd2UgY291bGQgYWxzbyB1c2UgdGhlIGBzdHJfc3ViYCBmdW5jdGlvbiBpbiBgc3RyaW5ncmAgdG8gZXh0cmFjdCBhICJzdWJzdHJpbmciIGZyb20gZWFjaCBlbnRyeSBpbiB0aGUgY29sdW1uLiBUaGUgYXJndW1lbnQgYGVuZD0tM2Agc3BlY2lmaWVzIHRoYXQgdGhlIGV4dHJhY3Rpb24gc2hvdWxkIGVuZCB0aHJlZSBjaGFyYWN0ZXJzIGJlZm9yZSB0aGUgZW5kIG9mIGVhY2ggc3RyaW5nLgoKYGBge3J9Cm1lc3N5ICU+JSAKICBtdXRhdGUoaGVpZ2h0X2F0X2RpYWdub3Npcz1zdHJfc3ViKGhlaWdodF9hdF9kaWFnbm9zaXMsIGVuZD0tMykpICU+JSAKICAgIG11dGF0ZShoZWlnaHRfYXRfZGlhZ25vc2lzID0gYXMubnVtZXJpYyhoZWlnaHRfYXRfZGlhZ25vc2lzKSkgJT4lIAogIGFycmFuZ2UoaGVpZ2h0X2F0X2RpYWdub3NpcykKYGBgCgojIyBGaW5hbCBjb2RlIHRvIGNsZWFuIHRoZSBkYXRhCgpGb3IgcmVmZXJlbmNlLCBoZXJlIGlzIHRoZSBmaW5hbCBjb2RlIGNodW5rIHRoYXQgY2FuIGJlIHVzZWQgdG8gY2xlYW4gdGhlIGRhdGEuCgpgYGB7cn0KY2xlYW5lZCA8LSByZWFkX3RzdigidGNnYV9jbGluaWNhbF9NRVNTWS50c3YiLCBuYSA9IGMoIk5VTEwiLCJOQSIpKSAlPiUgCiAgbXV0YXRlKG1lc3N5LCBnZW5kZXIgPSBmb3JjYXRzOjpmY3RfcmVjb2RlKGdlbmRlciwiTUFMRSI9Im1hbGUiKSwKICAgICAgIGdlbmRlciA9IGZvcmNhdHM6OmZjdF9yZWNvZGUoZ2VuZGVyLCJGRU1BTEUiPSJmZW1hbGUiKSkgJT4lIAogICAgbXV0YXRlKGhlaWdodF9hdF9kaWFnbm9zaXM9c3RyX3N1YihoZWlnaHRfYXRfZGlhZ25vc2lzLCBlbmQ9LTMpKSAlPiUgCiAgICBtdXRhdGUoaGVpZ2h0X2F0X2RpYWdub3NpcyA9IGFzLm51bWVyaWMoaGVpZ2h0X2F0X2RpYWdub3NpcykpCmBgYAoK